#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "QSerialPortInfo"//串口相关
#include <QSerialPort>//串口相关
#include <QMessageBox>
#include <QDateTime>

#include <QListView>//下拉框可变宽度函数



#include <QComboBox>
#include <QTimer>
#include <QSerialPortInfo>
#include <QMainWindow>
#include <QMenuBar>
#include <QAction>
#include <QCoreApplication>
#include <QSettings>  // 必须包含此头文件

#include <QRegularExpressionValidator>

#include "qcustomplot.h"  // 必须包含 QCustomPlot 头文件

#include "UI/ui_styles.h"    // 使用相对路径（从项目根目录出发）

// 定时发送-定时器
QTimer *timSend;
QTimer timer;//定时器 检测串口以及SPEED
QTimer *timer_line = new QTimer();//堆分配 定时器
int DataDecode = 1;//1--UTF-8  2--GB2312  3--ASCII
 QString versions = "V2.1";
static QRegularExpression newlineRegex("(\r\n|\n\r|\n|\r)");
 uint8_t open_uart = 0;



/**
 * @brief adjustComboBoxWidth
 * @param comboBox
 * @note 下拉框可变宽度函数
 */
void adjustComboBoxWidth(QComboBox *comboBox) {
    int maxWidth = 0;

    for (int i = 0; i < comboBox->count(); ++i) {
        int width = comboBox->fontMetrics().horizontalAdvance(comboBox->itemText(i));
        if (width > maxWidth) {
            maxWidth = width;
        }
    }

    QAbstractItemView *view = comboBox->view();
    if (view) {
        QListView *listView = qobject_cast<QListView*>(view);
        if (listView) {
            listView->setMinimumWidth(maxWidth + 10); // 加上一些额外的边距
        }
    }
}

/**
 * @brief updateSerialPorts
 * @param comboBox
 * @note 更新下拉框内容且不改变选中项
 */
void updateSerialPorts(QComboBox *comboBox) {
    if (!comboBox) {
        return; // 如果 comboBox 为空，直接返回
    }

    // 保存当前选中的项
    QString currentText = comboBox->currentText();

    // 清空当前列表
    comboBox->clear();

    // 获取当前可用的串口列表
    const auto ports = QSerialPortInfo::availablePorts();
    for (const auto &port : ports) {
        comboBox->addItem(port.description() + "(" + port.portName() + ")");
    }
    adjustComboBoxWidth(comboBox);
    comboBox->show();


    // 恢复选中的项
    if (!currentText.isEmpty()) {
        int index = comboBox->findText(currentText);
        if (index != -1) {
            comboBox->setCurrentIndex(index); // 设置为之前选中的项
        }
    }
}


//恢复关闭应用前的设置--与组件相关，不做优化
void MainWindow::restoreComboBoxSettings()
{
    QSettings settings("./custom.ini", QSettings::IniFormat);

    const QList<QPair<QComboBox*, QString>> comboBoxes =
    {
        {ui->serailCb,    "serailCb/CurrentText"},
        {ui->baundrateCb, "baundrateCb/CurrentText"},
        {ui->databitCb,   "databitCb/CurrentText"},
        {ui->checkbitCb,  "checkbitCb/CurrentText"},
        {ui->stopbitCb,   "stopbitCb/CurrentText"}
    };
    for (const auto& pair : comboBoxes) {
        QString savedValue = settings.value(pair.second).toString();
        int index = pair.first->findText(savedValue);
        if (index >= 0) {
            pair.first->setCurrentIndex(index);
        }
    }
    int fontSize = settings.value("Font/Size", 12).toInt(); // 默认值 12（如果键不存在）
    qDebug() << fontSize;
    // 设置 recvEdit 的字体大小
    QFont font = ui->recvEdit->font();
    font.setPointSize(fontSize); // 恢复保存的大小
    ui->recvEdit->setFont(font);

    font = ui->sendEdit->font();
    font.setPointSize(fontSize); // 恢复保存的大小
    ui->sendEdit->setFont(font);
}



// void computeFFT(const QVector<double>& inputSignal, QVector<double>& spectrum) {
//     int N = inputSignal.size();
//     // FFTW输入/输出数组
//     fftw_complex *in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
//     fftw_complex *out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
//     // 创建FFT计划
//     fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
//     // 填充输入数据（实部=信号，虚部=0）
//     for (int i = 0; i < N; ++i) {
//         in[i][0] = inputSignal[i]; // 实部
//         in[i][1] = 0.0;            // 虚部
//     }
//     // 执行FFT
//     fftw_execute(plan);
//     // 计算幅值谱
//     spectrum.resize(N/2 + 1);
//     for (int i = 0; i <= N/2; ++i) {
//         spectrum[i] = sqrt(out[i][0]*out[i][0] + out[i][1]*out[i][1]);
//     }
//     // 释放资源
//     fftw_destroy_plan(plan);
//     fftw_free(in);
//     fftw_free(out);
// }

/**
 * @brief 计算信号的FFT（支持自定义点数）
 * @param inputSignal 输入信号（长度可以不等于N）
 * @param spectrum 输出频谱（幅值，长度 = N/2 + 1）
 * @param N 自定义FFT点数（必须是2的幂次，若非则自动调整）
 */
void computeFFT(const QVector<double>& inputSignal, QVector<double>& spectrum, int N = -1) {
    // 1. 自动确定N（若未指定）
    if (N <= 0) {
        N = inputSignal.size(); // 默认使用信号长度
    }
    // 2. 调整N为最近的2的幂次（可选，提升FFT速度）
    // N = qNextPowerOfTwo(N); // Qt提供的函数，或者用自定义实现
    qDebug() << "fft_n" << N;
    // 3. 分配FFTW输入/输出数组
    fftw_complex *in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
    fftw_complex *out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);

    // 4. 创建FFT计划
    fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
    // 5. 填充输入数据（零填充或截断）
    const int signalSize = inputSignal.size();
    const int startIdx = (signalSize > N) ? (signalSize - N) : 0; // 截断起点
    for (int i = 0; i < N; ++i) {
        if (startIdx + i < signalSize) {
            in[i][0] = inputSignal[startIdx + i]; // 从后半部分开始取数据
            in[i][1] = 0.0;
        } else {
            in[i][0] = 0.0; // 不足时补零
            in[i][1] = 0.0;
        }
    }
    // 6. 执行FFT
    fftw_execute(plan);
    // 7. 计算幅值谱（仅保留前N/2+1点）
    spectrum.resize(N / 2 + 1);
    for (int i = 0; i <= N / 2; ++i) {
        spectrum[i] = std::sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
    }
    // 8. 释放资源
    fftw_destroy_plan(plan);
    fftw_free(in);
    fftw_free(out);
}




// File: mainwindow.cpp
// QVector<double> MainWindow::generateSignal(int length, double sampleRate) {

    // QVector<double> signal(length);
    // double freq1 = 5.0;   // 5Hz 正弦波
    // double freq2 = 20.0;  // 20Hz 方波
    // for (int i = 0; i < length; ++i) {
    //     double t = i / sampleRate;
    //     // 生成一个混合信号（5Hz正弦 + 20Hz方波 + 随机噪声）
    //     signal[i] = qSin(2 * M_PI * freq1 * t)
    //                 + (qSin(2 * M_PI * freq2 * t) > 0 ? 1.0 : -1.0)
    //                 + (QRandomGenerator::global()->generateDouble() * 0.2 - 0.1);
    // }
    // return signal;


// }

// 在构造函数或初始化函数中设置验证器
void MainWindow::setupHexValidator()
{
    // 创建正则表达式：允许0-9、a-f、A-F，可选的"0x"前缀
    QRegularExpression hexRegExp("^[0-9A-Fa-f]+$");

    // 应用到目标控件（假设是 ui->CmdEdit）
    QRegularExpressionValidator *validator = new QRegularExpressionValidator(hexRegExp, this);
    ui->CmdEdit->setValidator(validator);
    ui->dw0Edit->setValidator(validator);
    ui->dw1Edit->setValidator(validator);
    ui->dw2Edit->setValidator(validator);
    ui->dw3Edit->setValidator(validator);
    ui->dw4Edit->setValidator(validator);
    ui->dw5Edit->setValidator(validator);
    ui->dw6Edit->setValidator(validator);
    ui->dw7Edit->setValidator(validator);
    // 可选：设置输入提示
    ui->CmdEdit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw0Edit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw1Edit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw2Edit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw3Edit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw4Edit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw5Edit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw6Edit->setPlaceholderText("Hex(0-9,A-F)");
    ui->dw7Edit->setPlaceholderText("Hex(0-9,A-F)");

    // 设置接收框的多行提示文本（支持HTML富文本）
    QString welcomeMsg =
        "Welcome to Embedded Communicator (Qt 6.5.3) \n\n"
        "** Usage Guide **  \n"
        "1. Data Logging:\n"
        "   - For large data transfers, enable LOG recording\n"
        "   - Log path: ./log/comm_YYYYMMDD.log\n\n"
        "2. Chart Data Format:\n"
        "   (line0,line1,line2,...,line15\\r\\n)\n"
        "   - Max 16 lines, comma separated\n"
        "   - Example: (1024,2048,1536\\r\\n)\n\n"
        "3. Command Protocol:\n"
        "   Format: CMD + DW0~DW7\n"
        "   Example: cmdAABB + 1122 + 3344 + ... + FFEE\n"
        "   - DWx must be 4-byte HEX (e.g. 00FF)";
    ui->recvEdit->setPlaceholderText(welcomeMsg);
    ui->recvEdit->setToolTip(welcomeMsg); // 悬浮提示同样生效
}




// 实现事件过滤器：
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {

    if (obj == ui->qCustomPlot && event->type() == QEvent::Wheel) {
        QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
        if (wheelEvent->modifiers() & Qt::ControlModifier) {
            // Ctrl+滚轮：仅缩放Y轴
            ui->qCustomPlot->axisRect()->setRangeZoom(Qt::Vertical);
        } else {
            // 普通滚轮：仅缩放X轴
            ui->qCustomPlot->axisRect()->setRangeZoom(Qt::Horizontal);
        }
    }
    else if (obj == ui->Test && event->type() == QEvent::Wheel) {
        QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
        if (wheelEvent->modifiers() & Qt::ControlModifier) {
            // Ctrl+滚轮：仅缩放Y轴
            ui->Test->axisRect()->setRangeZoom(Qt::Vertical);
        } else {
            // 普通滚轮：仅缩放X轴
            ui->Test->axisRect()->setRangeZoom(Qt::Horizontal);
        }
    }

    if (obj == ui->qCustomPlot )
    {
        if (event->type() == QEvent::Leave) {
            // 鼠标离开 customPlot
            hLine->setVisible(false);
            vLine->setVisible(false);
            ui->qCustomPlot->replot();
            qDebug()<< "lev";
            return true;
        } else if (event->type() == QEvent::Enter) {
            // 鼠标进入 customPlot
            hLine->setVisible(true);
            vLine->setVisible(true);
            ui->qCustomPlot->replot();
            qDebug()<< "innnn";
            return true;
        }
    }

    return QMainWindow::eventFilter(obj, event);


}

//窗口创建初始化函数
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);//默认
    set_light_dark_StyleSheet("styles_light.qss","styles_dark.qss");
    QSettings settings("./custom.ini", QSettings::IniFormat);
    isDarkMode = settings.value("DarkMode", false).toBool(); //确定是dark还是light
    setDarkMode(this, isDarkMode);//设置
    Set_qCustomPlot_Dark(isDarkMode);


    /*******输入编辑框初始化*********/
    ui->Plot_num_Edit->setValidator(new QIntValidator(0, 99999, this));//画图点数
    ui->FFTNum_Edit->setValidator(new QIntValidator(0, 99999, this));//fft计算点数
    ui->DataFs_Edit->setValidator(new QIntValidator(0, 999999, this));//数据的采样频率点数
    setupHexValidator();

    /*******绘图初始化*********/
    //十字线（水平和垂直线）
    hLine = new QCPItemStraightLine(ui->qCustomPlot);
    vLine =new QCPItemStraightLine(ui->qCustomPlot);
    // 创建数据标签
    dataLabel = new QCPItemText(ui->qCustomPlot);
    dataLabel->setLayer("overlay");

    // 初始化十字线样式
    hLine->setPen(QPen(QColor(180, 180, 180), 1, Qt::DashLine));
    vLine->setPen(QPen(QColor(180, 180, 180), 1, Qt::DashLine));
    hLine->setClipToAxisRect(false);
    vLine->setClipToAxisRect(false);

    // 初始化标签样式
    dataLabel->setPositionAlignment(Qt::AlignLeft|Qt::AlignTop);
    dataLabel->position->setType(QCPItemPosition::ptAbsolute);
    dataLabel->setFont(QFont("Arial", 9));
    dataLabel->setPen(QPen(Qt::black));
    dataLabel->setBrush(QBrush(QColor(255, 255, 255, 200)));
    dataLabel->setPadding(QMargins(3,3,3,3));


    // 连接鼠标移动信号
    connect(ui->qCustomPlot, &QCustomPlot::mouseMove, this, [=](QMouseEvent *event) {

        // isMouseInPlot = true;

        double mouseX = ui->qCustomPlot->xAxis->pixelToCoord(event->pos().x());
        double mouseY = ui->qCustomPlot->yAxis->pixelToCoord(event->pos().y());

        // 更新十字线位置
        hLine->point1->setCoords(ui->qCustomPlot->xAxis->range().lower, mouseY);
        hLine->point2->setCoords(ui->qCustomPlot->xAxis->range().upper, mouseY);
        vLine->point1->setCoords(mouseX, ui->qCustomPlot->yAxis->range().lower);
        vLine->point2->setCoords(mouseX, ui->qCustomPlot->yAxis->range().upper);

        // 移动标签
        const int labelOffset = 15;
        QPoint labelPos = event->pos() + QPoint(labelOffset, 0);

        if (labelPos.x() > ui->qCustomPlot->width() - 100) {
            labelPos = event->pos() - QPoint(100, 0);
        }

        dataLabel->position->setCoords(labelPos);

        // 收集数据点
        QStringList yValues;
        bool hasExactData = false;

        for (int i = 0; i < ui->qCustomPlot->graphCount(); ++i) {
            QCPGraph *graph = ui->qCustomPlot->graph(i);
            if (!graph || !graph->visible()) continue;

            if (auto data = graph->data()) {
                if (auto closest = data->findBegin(mouseX)) {
                    QPointF pixelPos = {
                        ui->qCustomPlot->xAxis->coordToPixel(closest->key),
                        ui->qCustomPlot->yAxis->coordToPixel(closest->value)
                    };

                    if (QLineF(pixelPos, event->pos()).length() < 15) {
                        yValues << QString("Y%1: %2").arg(i+1).arg(closest->value, 0, 'f', 3);
                        hasExactData = true;
                    }
                }
            }
        }

        dataLabel->setText(hasExactData
                               ? QString("X: %1\n%2").arg(mouseX, 0, 'f', 3).arg(yValues.join("\n"))
                               : QString("X: %1\nY: %2").arg(mouseX, 0, 'f', 3).arg(mouseY, 0, 'f', 3));

        ui->qCustomPlot->replot(QCustomPlot::rpQueuedReplot);
    });




    ui->qCustomPlot->installEventFilter(this); // 安装事件过滤器
    ui->Test->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    ui->qCustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    // ui->Test->installEventFilter(this); // 安装事件过滤器



    ui->sendEdit->font();//修改一下这个防止字体出问题
    ui->recvEdit->setLineWrapMode(QPlainTextEdit::NoWrap);  // 不自动换行

    QString path = QCoreApplication::applicationDirPath();//获取地址
    setWindowTitle("QtComTool-作者:EleWu "+ versions + "[UTF_8] "+ path); // 动态修改窗口标题



/**********菜单的活动构建*************/
    //****Menu--actionASCII
    connect(ui->actionASCII, &QAction::triggered, this, [this]{

        QString path = QCoreApplication::applicationDirPath();
        setWindowTitle("QtComTool-作者:EleWu "+ versions + "[ASCII] " + path); // 动态修改窗口标题
        DataDecode = 3;
    });
    //****Menu--actionGB2312
    connect(ui->actionGB2312, &QAction::triggered, this, [this]{
        QString path = QCoreApplication::applicationDirPath();
        setWindowTitle("QtComTool-作者:EleWu "+ versions + "[GB2312] "+ path); // 动态修改窗口标题
        DataDecode = 2;
    });
    //****Menu--actionUTF_8
    connect(ui->actionUTF_8, &QAction::triggered, this, [this]{
        QString path = QCoreApplication::applicationDirPath();
        setWindowTitle("QtComTool-作者:EleWu "+ versions + "[UTF_8] "+ path); // 动态修改窗口标题
        DataDecode = 1;
    });
    //****Menu--actionUTF_8
    connect(ui->actionDark, &QAction::triggered, this, [this]{
        // qApp->setStyleSheet(darkStyleSheet); // 全局应用 Dark Mode
        isDarkMode = true;  // 记录当前模式
        QSettings settings("./custom.ini", QSettings::IniFormat);
        settings.setValue("DarkMode", isDarkMode);
        ui->sendEdit->font();
        // setDarkMode(isDarkMode);
        setDarkMode(this, isDarkMode);
        Set_qCustomPlot_Dark(isDarkMode);
    });

    connect(ui->actionLight, &QAction::triggered, this, [this]{
        isDarkMode = false;  // 记录当前模式

        QSettings settings("./custom.ini", QSettings::IniFormat);
        settings.setValue("DarkMode", isDarkMode);
        // qApp->setStyleSheet(lightStyleSheet); // 全局应用 Light Mode
        // setDarkMode(isDarkMode);
        setDarkMode(this, isDarkMode);
        Set_qCustomPlot_Dark(isDarkMode);

        ui->sendEdit->font();
    });

    connect(ui->actionFont_Size_10, &QAction::triggered, this, [this]{
        isDarkMode = false;  // 记录当前模式
        uint8_t FontSize = 10;

        // 获取当前字体，修改大小后再设置回去
        QFont font = ui->recvEdit->font();
        font.setPointSize(FontSize); // 字体大小设置为 12 磅
        ui->recvEdit->setFont(font); // 应用新字体
        ui->sendEdit->setFont(font);
        qDebug() << ui->sendEdit->font();


        QSettings settings("./custom.ini", QSettings::IniFormat);
        settings.setValue("Font/Size", ui->recvEdit->font().pointSize()); // 保存当前字体大小
    });

    connect(ui->actionFont_Size_12, &QAction::triggered, this, [this]{
        isDarkMode = false;  // 记录当前模式
        uint8_t FontSize = 12;

        // 获取当前字体，修改大小后再设置回去
        QFont font = ui->recvEdit->font();
        font.setPointSize(FontSize); // 字体大小设置为 12 磅
        ui->recvEdit->setFont(font); // 应用新字体
        ui->sendEdit->setFont(font);
        qDebug() << ui->sendEdit->font();


        QSettings settings("./custom.ini", QSettings::IniFormat);
        settings.setValue("Font/Size", ui->recvEdit->font().pointSize()); // 保存当前字体大小
    });

    connect(ui->actionFont_Size_14, &QAction::triggered, this, [this]{
        isDarkMode = false;  // 记录当前模式
        uint8_t FontSize = 14;

        // 获取当前字体，修改大小后再设置回去
        QFont font = ui->recvEdit->font();
        font.setPointSize(FontSize); // 字体大小设置为 12 磅
        ui->recvEdit->setFont(font); // 应用新字体
        ui->sendEdit->setFont(font);
        qDebug() << ui->sendEdit->font();

        QSettings settings("./custom.ini", QSettings::IniFormat);
        settings.setValue("Font/Size", ui->recvEdit->font().pointSize()); // 保存当前字体大小

    });

    connect(ui->actionTop, &QAction::triggered, this, [this]{

            bool isTopMost = windowFlags() & Qt::WindowStaysOnTopHint;
            setWindowFlag(Qt::WindowStaysOnTopHint, !isTopMost); // 单独设置标志位
            show(); // 必须调用以生效
            // 更新菜单标题
            ui->actionTop->setText(isTopMost ? "TOP" : "IsTOP");

    });

    connect(ui->actionOpacity_80, &QAction::triggered, this, [this]{
        enableAcrylicEffect(this,0.8);
    });
    connect(ui->actionOpacity_90, &QAction::triggered, this, [this]{
        enableAcrylicEffect(this,0.9);
    });
    connect(ui->actionOpacity_100, &QAction::triggered, this, [this]{
        enableAcrylicEffect(this,1);
    });
    // QStringList serialNamePort;


    serialPort = new QSerialPort(this);
    //意味着收到消息之后，会进入这个槽函数进行处理
    serialPort->setReadBufferSize(512);//512byte的缓存
    connect(serialPort,SIGNAL(readyRead()),this,SLOT(manual_serialPortReadyRead()));/*手动连接槽函数*/
    connect(serialPort, &QSerialPort::errorOccurred, this, [this](QSerialPort::SerialPortError error){
        qDebug() << error;
         if (error == QSerialPort::ResourceError|| error == QSerialPort::PermissionError) {
             qDebug() << "Port disconnected! Attempting to reconnect...";
             open_uart = 2;
             serialPort->close();
         }
    });



    connect(timer_line,&QTimer::timeout,this,[this](){
        // qDebug() << "do_line";
        do_line = 1;
        timer_line->stop();
    });//定时器超时之后设置值为1



    ui->serailCb->clear();
    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        ui->serailCb->addItem(info.description() + "(" + info.portName() + ")");
    }



    updateSerialPorts(ui->serailCb);
    ui->serailCb->show();

    restoreComboBoxSettings();

    timer.setInterval(1000); // 每秒检测一次
    QObject::connect(&timer, &QTimer::timeout, [&]()
    {
        long temp_recvNum;
        qDebug()<<ui->openBt->text();
        qDebug()<<open_uart;
        if(open_uart == 0)
        {
            updateSerialPorts(ui->serailCb);
        }
        else if((open_uart == 2) && (ui->openBt->text() != "打开串口"))
        {
            //断开连接 尝试重新连接
            if(serialPort->open(QIODevice::ReadWrite) == true)
            {
                open_uart = 1;
                //statusBar 状态栏显示端口状态
                QString sm = "%1 Opened, baund:%2, bit:%3";
                status = sm.arg(serialPort->portName()).arg(serialPort->baudRate()).arg(serialPort->dataBits());
                lblPortState->setText(status);
                lblPortState->setStyleSheet("color:green");
                ui->openBt->setStyleSheet(
                    "QPushButton {"
                    "   background-color: rgb(0, 128, 0);"  // 深绿色背景
                    "   color: white;"                      // 白色文字
                    "   border-radius: 6px;"                // 圆角
                    "   padding: 8px 15px;"                 // 内边距（上下8px，左右15px）
                    "   border: 1px solid #006400;"         // 深绿色边框（可选）
                    "}"
                    "QPushButton:hover {"
                    "   background-color: rgb(0, 160, 0);"  // 悬停时稍亮的绿色
                    "}"
                    "QPushButton:pressed {"
                    "   background-color: rgb(0, 100, 0);"  // 按下时更深的绿色
                    "}"
                    );
            }
            else
            {
                lblPortState->setStyleSheet("color:red");
                QString sm = "%1 Loss, baund:%2, bit:%3";
                status = sm.arg(serialPort->portName()).arg(serialPort->baudRate()).arg(serialPort->dataBits());
                lblPortState->setText(status);
                ui->openBt->setStyleSheet(
                    "QPushButton {"
                    "   background-color: rgb(128, 0, 0);"  // 深绿色背景
                    "   color: white;"                      // 白色文字
                    "   border-radius: 6px;"                // 圆角
                    "   padding: 8px 15px;"                 // 内边距（上下8px，左右15px）
                    "   border: 1px solid #640000;"         // 红
                    "}"
                    "QPushButton:hover {"
                    "   background-color: rgb(160, 0, 0);"  // 红
                    "}"
                    "QPushButton:pressed {"
                    "   background-color: rgb(100, 0, 0);"  // 红
                    "}"
                    );

            }
        }

        if(old_recvNum <= recvNum)
        {
        temp_recvNum = recvNum - old_recvNum;
        double speed_kb = temp_recvNum / 1024.0;
        ui->speedLabel->setText(QString("SPEED: %1 KB/S").arg(speed_kb, 0, 'f', 2));
        }
        old_recvNum = recvNum;

    });
    timer.start();


    // 发送、接收计数清零
    sendNum = 0;
    recvNum = 0;

    // 状态栏
    QStatusBar *sBar = statusBar();
    // 状态栏的收、发计数标签
    lblSendNum = new QLabel(this);
    lblRecvNum = new QLabel(this);
    lblPortState = new QLabel(this);
    lblPortState->setText("Connected");
    //设置串口状态标签为绿色 表示已连接状态
    lblPortState->setStyleSheet("color:red");
    // 设置标签最小大小
    lblSendNum->setMinimumSize(200, 20);
    lblRecvNum->setMinimumSize(200, 20);
    lblPortState->setMinimumSize(250, 20);
    setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
    setNumOnLabel(lblRecvNum, "已接收(字节): ", recvNum);
    // 从右往左依次添加
    sBar->addWidget(lblPortState);
    sBar->addPermanentWidget(lblSendNum);
    sBar->addPermanentWidget(lblRecvNum);

















    // 定时发送-定时器
    timSend = new QTimer;
    timSend->setInterval(1000);// 设置默认定时时长1000ms
    connect(timSend, &QTimer::timeout, this, [=](){
        on_sendBt_clicked();
    });


}


MainWindow::~MainWindow()
{
    delete ui;
}


//槽函数

//检测通讯端口槽函数
// void MainWindow::on_btnSerialCheck_clicked()
// {

//     ui->serailCb->clear();
//     //通过QSerialPortInfo查找可用串口
//     foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
//     {
//         ui->serailCb->addItem(info.description() + "(" + info.portName() + ")");
//         qDebug() << "串口名称：" << info.portName();
//         qDebug() << "串口描述：" << info.description();
//         qDebug() << "制造商：" << info.manufacturer();
//     }


//     adjustComboBoxWidth(ui->serailCb);
//     ui->serailCb->show();
// }


/*
 * UTF-8编码背景知识：
UTF-8中，1字节字符以0开头
2字节字符以110开头
3字节字符以1110开头
4字节字符以11110开头
后续字节都以10开头
E4 BD A0 : UTF-8编码的"你"(完整3字节)
E5 A5 BD : UTF-8编码的"好"(完整3字节)
E5 95 8A : UTF-8编码的"啊"(完整3字节)
EF BC 8C : UTF-8编码的"，"(完整3字节)
E4 B8 96 : UTF-8编码的"世"(完整3字节)
E7 95 8C : UTF-8编码的"界"(完整3字节)
EF BC 81 : UTF-8编码的"！"(完整3字节)
*/

// //检查接收数据尾部不完整字符的字节数，返回不完整字节个数
int MainWindow::checkReadBuf(const QByteArray &ReadBuf){
    int count=0;
    if((ReadBuf.at(ReadBuf.size()-1) & 0x80) == 0)
    {
        // ASCII字符 (0xxxxxxx)
        return 0;
    }

    if((ReadBuf.at(ReadBuf.size()-1) & 0xC0) == 0xC0)
    {
        //字节头
        return 1;
    }


    count = 1;
    if((ReadBuf.at(ReadBuf.size()-2) & 0xC0) == 0X80)
    {
        //判断第二个不是字节头
        count=2;

        if(!(ReadBuf.at(ReadBuf.size()-3) & 0x40))
        {
            //判断第三个字节不是字节头
            return 0;//可能第四个字节是，但是第四个字节是的话也是一个完整的编码
        }
    }


    switch(count)
    {
    case 0:return 0;break;
    case 1://第二个是字节头
        //判断是 3 4 字节 UTF-8 的头吗
        if((ReadBuf.at(ReadBuf.size()-2) & 0xE0) == 0xE0)
        {
            return 2;
        }
        else{
            return 0;
        }
        break;
    case 2:
        //判断是  4 字节 UTF-8 的头吗
        if((ReadBuf.at(ReadBuf.size()-3) & 0xF0) == 0xF0)
        {

            return 3;
        }
        else{
            return 0;
        }
        break;
    }
    return 0;
}


// GB2312不完整字符检查
int checkGb2312Incomplete(const QByteArray& readBuf) {
    if (readBuf.isEmpty()) return 0;

    unsigned char lastByte = readBuf.at(readBuf.size() - 1);

    // 完整ASCII字符
    if (lastByte < 0x80) return 0;

    // 如果是GB2312双字节字符的第一个字节
    if (readBuf.size() == 1) return 1;

    unsigned char prevByte = readBuf.at(readBuf.size() - 2);

    // 前一个字节是GB2312首字节
    if (prevByte >= 0xA1 && prevByte <= 0xF7) {
        if (lastByte >= 0xA1 && lastByte <= 0xFE) {
            return 0;  // 完整双字节
        }
        return 1;  // 第二个字节无效
    }

    // 当前字节应该属于双字节的第二个字节
    if (lastByte >= 0xA1 && lastByte <= 0xFE) {
        return 1;  // 缺少第一个字节
    }

    return 0;  // 不匹配GB2312
}


// 换行检查
int checkRN(const QByteArray& readBuf) {
    if (readBuf.isEmpty()) return 0;

    unsigned char lastByte = readBuf.at(readBuf.size() - 1);

    // 完整ASCII字符
    if (lastByte  ==  '\r') return 1;

    return 0;
}




int ResByte=0;
QByteArray ReadTemp;
/*手动实现接收数据函数-需要连接-串口接收处理函数*/
void MainWindow::manual_serialPortReadyRead()
{

    if (serialPort->bytesAvailable() <= 0) {
        return;
    }

    QByteArray recBuf = serialPort->readAll();//获取串口所有数据
    int dataLen = recBuf.length();
    if (dataLen <= 0)
    {
        return;
    }

    QString str_rev = "";

    // 接收字节计数
    recvNum += recBuf.size();
    // 状态栏显示计数值
    setNumOnLabel(lblRecvNum, "已接收(字节):", recvNum);

    // 换行咯
    if((ui->chk_rev_line->checkState() == Qt::Checked) && (ResByte == 0)  && (1 == do_line))
    {
        // timer_line->stop();//如果 do_line = 1自然会停止
        do_line = 0;
        str_rev = ("\r\n");
        timer_line->start(ui->txtline_ms->text().toInt());// 设置定时时长，重新计数
        //加时间戳
        if(ui->chk_rev_time->checkState() == Qt::Checked )
        {
            QDateTime nowtime = QDateTime::currentDateTime();
            str_rev +="[" + nowtime.toString("yy-MM-dd HH:mm:ss.zzz") + "]";
            if(ui->chk_rev_hex->checkState() ==  Qt::Checked)
            {
                str_rev += "[HEX]:";
            }
        }
    }


    //1.判断是文本还是HEX文件
    if(ui->chk_rev_hex->checkState() == false)
    {
        //文本




        switch(DataDecode)
        {
        case 1://UTF-8

            ResByte = checkReadBuf(recBuf);
            ReadTemp += recBuf;
            if(ResByte != 0)//去除尾部不完整字符
            {
                ReadTemp.resize(ReadTemp.size() - ResByte);
            }
            else
            {
                ResByte =  checkRN(recBuf);
                if(ResByte != 0)//去除尾部不完整字符
                {
                    ReadTemp.resize(ReadTemp.size() - ResByte);
                }


            }

            str_rev += QString(ReadTemp);//utf
            break;
        case 2://gb2312

            ResByte = checkGb2312Incomplete(recBuf);
            ReadTemp += recBuf;
            if(ResByte != 0)//去除尾部不完整字符
            {
                ReadTemp.resize(ReadTemp.size() - ResByte);
            }
            else
            {
                ResByte =  checkRN(recBuf);
                if(ResByte != 0)//去除尾部不完整字符
                {
                    ReadTemp.resize(ReadTemp.size() - ResByte);
                }
            }



            str_rev +=  QString::fromLocal8Bit(ReadTemp.data());
            break;
        case 3:
            str_rev += QString(ReadTemp);//utf
            break;
        }


        //1.2.判断是否打开时间戳 且没打开自动换行
        if((ui->chk_rev_time->checkState() == Qt::Checked) && (ui->chk_rev_line->checkState() == Qt::Unchecked))
        {
            // 检查是否有换行符，并在每一行前面加上时间戳
            volatile static uint8_t do_time = 1;
            // 定义换行符的正则表达式

            QString tempString = str_rev;
            QStringList lines = tempString.split(newlineRegex, Qt::SkipEmptyParts);//分割成lines
            QDateTime now = QDateTime::currentDateTime();
            QString timestamp = now.toString("yy-MM-dd HH:mm:ss.zzz");

            QStringList timestampedLines;

            str_rev = "";
            if ((tempString[0] == QChar('\n') || tempString[0] == QChar('\r')) && do_time == 0)
            {
                //是换行，添加换行
                str_rev.append("\r\n[")
                    .append(timestamp)
                    .append("]:");
            }



            qDebug() << tempString;
            foreach (const QString &line, lines)
            {
                if(do_time == 1)
                    timestampedLines.append("[" +timestamp + "]:" + line);
                else
                {
                    timestampedLines.append(line);
                    do_time = 1;
                }
            }

            str_rev += timestampedLines.join("\r\n");
            // 检查是否还有剩余的数据
            if (!tempString.endsWith("\n") && !tempString.endsWith("\r"))
            {
                //意味着最后一个不是换行
                do_time = 0;
            }
            else
            {
                //是换行，添加换行
                str_rev += "\r\n";
            }
            qDebug() << str_rev;
        }
        ReadTemp.clear();
        if(ResByte != 0)
        {
            for(int i = 0;i < ResByte;i++)
            {
                ReadTemp.push_back(recBuf.at(recBuf.size() - ResByte + i));//把不完整的字节添加到缓冲头部留给下次拼接
            }
        }
    }
    else
    {
        // 16进制显示，并转换为大写
        QString str1 = recBuf.toHex().toUpper();
        // 添加空格
        QString str2;
        for(int i = 0; i<str1.length (); i+=2)
        {
            str2 += str1.mid (i,2);
            str2 += " ";
        }
        str_rev += str2;
    }

    QString filetext = str_rev;
    if (isFileMode)//存储到Log里
    {
        // 文件模式：写入磁盘
        filetext.replace("\r\n", "\n");
        logFile.write(filetext.toUtf8());
    }

    // Check if stopping display is enabled
    if (ui->chk_rev_stop->checkState() == Qt::Checked)
    {
        pausedDataBuffer.append(str_rev);  // 缓存数据
        str_rev.clear();
        return; // If checkbox is checked, don't process or display data
    }
    // ui->recvEdit->moveCursor(QTextCursor::End);
    // 3. 恢复显示时，先处理缓存数据（如果有）
    if (!pausedDataBuffer.isEmpty())
    {
        appendDirect(pausedDataBuffer);
        pausedDataBuffer.clear();
    }
    // 4. 将现有数据显示

    appendDirect(str_rev);
    str_rev.clear();
    return;

}

/*打开串口*/
void MainWindow::on_openBt_clicked()
{
    /*串口初始化*/
    QSerialPort::BaudRate baudRate = QSerialPort::Baud115200;
    QSerialPort::DataBits dataBits = QSerialPort::Data8;
    QSerialPort::StopBits stopBits = QSerialPort::OneStop;
    QSerialPort::Parity checkBits = QSerialPort::NoParity;


    QSettings settings("./custom.ini", QSettings::IniFormat);
    settings.setValue("serailCb/CurrentText", ui->serailCb->currentText());
    settings.setValue("baundrateCb/CurrentText", ui->baundrateCb->currentText());
    settings.setValue("databitCb/CurrentText", ui->databitCb->currentText());
    settings.setValue("checkbitCb/CurrentText", ui->checkbitCb->currentText());
    settings.setValue("stopbitCb/CurrentText", ui->stopbitCb->currentText());

    // 获取串口波特率
    // baudRate = ui->baundrateCb->currentText().toInt();直接字符串转换为 int 的方法

    if(ui->baundrateCb->currentText()=="1200")
        baudRate=QSerialPort::Baud1200;
    else if(ui->baundrateCb->currentText()=="2400")
        baudRate=QSerialPort::Baud2400;
    else if(ui->baundrateCb->currentText()=="4800")
        baudRate=QSerialPort::Baud4800;
    else if(ui->baundrateCb->currentText()=="9600")
        baudRate=QSerialPort::Baud9600;
    else if(ui->baundrateCb->currentText()=="19200")
        baudRate=QSerialPort::Baud19200;
    else if(ui->baundrateCb->currentText()=="38400")
        baudRate=QSerialPort::Baud38400;
    else if(ui->baundrateCb->currentText()=="57600")
        baudRate=QSerialPort::Baud57600;
    else if(ui->baundrateCb->currentText()=="115200")
        baudRate=QSerialPort::Baud115200;
    else if(ui->baundrateCb->currentText()=="256000")
        baudRate=QSerialPort::Baud256000;
    else if(ui->baundrateCb->currentText()=="512000")
        baudRate=QSerialPort::Baud512000;
    else if(ui->baundrateCb->currentText()=="750000")
        baudRate=QSerialPort::Baud750000;
    else if(ui->baundrateCb->currentText()=="921600")
        baudRate=QSerialPort::Baud921600;
    else if(ui->baundrateCb->currentText()=="1500000")
        baudRate=QSerialPort::Baud1500000;

    // 获取串口数据位
    if(ui->databitCb->currentText()=="5")
        dataBits=QSerialPort::Data5;
    else if(ui->databitCb->currentText()=="6")
        dataBits=QSerialPort::Data6;
    else if(ui->databitCb->currentText()=="7")
        dataBits=QSerialPort::Data7;
    else if(ui->databitCb->currentText()=="8")
        dataBits=QSerialPort::Data8;

    // 获取串口停止位
    if(ui->stopbitCb->currentText()=="1")
        stopBits=QSerialPort::OneStop;
    else if(ui->stopbitCb->currentText()=="1.5")
        stopBits=QSerialPort::OneAndHalfStop;
    else if(ui->stopbitCb->currentText()=="2")
        stopBits=QSerialPort::TwoStop;

    // 获取串口奇偶校验位
    if(ui->checkbitCb->currentText() == "none"){
        checkBits = QSerialPort::NoParity;
    }else if(ui->checkbitCb->currentText() == "奇校验"){
        checkBits = QSerialPort::OddParity;
    }else if(ui->checkbitCb->currentText() == "偶校验"){
        checkBits = QSerialPort::EvenParity;
    }else{
        checkBits = QSerialPort::NoParity;
    }

    QString selectedText = ui->serailCb->currentText(); // 获取当前选中的项的文本
    QRegularExpression re(R"(\(([^)]+)\))"); // 正则表达式，匹配括号内的内容
    QRegularExpressionMatch match = re.match(selectedText);

    if (match.hasMatch()) {
        QString portName = match.captured(1); // 提取串口号
        serialPort->setPortName(portName);
        qDebug() << "Selected Port Name:" << portName;
    } else {
        qDebug() << "No port name found in the selected text.";
    }



    // 初始化串口属性，设置 端口号、波特率、数据位、停止位、奇偶校验位数
    // serialPort->setPortName(ui->serailCb->currentText());

    serialPort->setBaudRate(baudRate);
    serialPort->setDataBits(dataBits);
    serialPort->setStopBits(stopBits);
    serialPort->setParity(checkBits);



    // 根据初始化好的串口属性，打开串口
    // 如果打开成功，反转打开按钮显示和功能。打开失败，无变化，并且弹出错误对话框。
    if(ui->openBt->text() == "打开串口"){
        if(serialPort->open(QIODevice::ReadWrite) == true)
        {
            open_uart = 1;
            //QMessageBox::
            ui->openBt->setText("关闭串口");
            // 让端口号下拉框不可选，避免误操作（选择功能不可用，控件背景为灰色）
            ui->serailCb->setEnabled(false);
            ui->baundrateCb->setEnabled(false);//波特率
            ui->databitCb->setEnabled(false);//数据位
            ui->checkbitCb->setEnabled(false);//奇偶
            ui->stopbitCb->setEnabled(false);//stopbitCb

            //statusBar 状态栏显示端口状态
            QString sm = "%1 Opened, baund:%2, bit:%3";
            status = sm.arg(serialPort->portName()).arg(serialPort->baudRate()).arg(serialPort->dataBits());
            lblPortState->setText(status);
            lblPortState->setStyleSheet("color:green");
            ui->openBt->setStyleSheet(
                "QPushButton {"
                "   background-color: rgb(0, 128, 0);"  // 深绿色背景
                "   color: white;"                      // 白色文字
                "   border-radius: 6px;"                // 圆角
                "   padding: 8px 15px;"                 // 内边距（上下8px，左右15px）
                "   border: 1px solid #006400;"         // 深绿色边框（可选）
                "}"
                "QPushButton:hover {"
                "   background-color: rgb(0, 160, 0);"  // 悬停时稍亮的绿色
                "}"
                "QPushButton:pressed {"
                "   background-color: rgb(0, 100, 0);"  // 按下时更深的绿色
                "}"
                );
        }
        else{
            QMessageBox::critical(this, "错误提示:", "串口打开失败！！！\r\n该串口可能被占用\r\n请选择正确的串口");
        }
    }else{
        open_uart = 0;
        qDebug()<<"off uart";
        serialPort->close();
        ui->openBt->setText("打开串口");
        // 端口号下拉框恢复可选，避免误操作
        ui->serailCb->setEnabled(true);
        ui->baundrateCb->setEnabled(true);//波特率
        ui->databitCb->setEnabled(true);//数据位
        ui->checkbitCb->setEnabled(true);//奇偶
        ui->stopbitCb->setEnabled(true);//stopbitCb


        //statusBar 状态栏显示端口状态
        QString sm = "%1 CLOSED";
        status = sm.arg(serialPort->portName());
        lblPortState->setText(status);
        lblPortState->setStyleSheet("color:red");
        ui->openBt->setStyleSheet("");
    }

}

/*发送数据*/
void MainWindow::on_sendBt_clicked()
{
    QByteArray array;

    //Hex复选框
    if(ui->chk_send_hex->checkState() == Qt::Checked){

        array = QByteArray::fromHex(ui->sendEdit->toPlainText().toUtf8()).data();
        qDebug() << "hex:"<<array.toHex();


    }else{
        array = ui->sendEdit->toPlainText().toLocal8Bit().data();
        switch(DataDecode)
        {
        case 1://UTF-8
            array = ui->sendEdit->toPlainText().toUtf8().data();
            break;
        case 2://gb2312
            array = ui->sendEdit->toPlainText().toLocal8Bit().data();
            break;
        case 3://ascii 未支持
            array = ui->sendEdit->toPlainText().toLocal8Bit().data();
            break;

        }

        qDebug() << "ASCII:"<<array.toHex();
    }

    if(ui->chk_send_line->checkState() == Qt::Checked){
        array.append("\r\n");
    }
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    int a = serialPort->write(array);
    // 发送字节计数并显示
    if(a > 0)
    {
        qDebug() << "a:"<<a;
        // 发送字节计数
        sendNum += a;
        // 状态栏显示计数值
        setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
    }
}
// 状态栏标签显示计数值
void MainWindow::setNumOnLabel(QLabel *lbl, QString strS, long num)
{
    // 标签显示
    QString strN;
    // strN.sprintf("%ld", num);
    strN = QString::number(num);
    QString str = strS + strN;
    lbl->setText(str);
}
/*清空*/
// void MainWindow::on_clearBt_clicked()
// {
//     ui->recvEdit->clear();
//     // 清除发送、接收字节计数
//     sendNum = 0;
//     recvNum = 0;
//     // 状态栏显示计数值
//     setNumOnLabel(lblSendNum, "已发送(字节):", sendNum);
//     setNumOnLabel(lblRecvNum, "已接收(字节):", recvNum);
// }

//清除发送的信息
void MainWindow::on_btnClearSend_clicked()
{
    ui->sendEdit->clear();
    // 清除发送字节计数
    sendNum = 0;
    // 状态栏显示计数值
    setNumOnLabel(lblSendNum, "已发送(字节):", sendNum);
}
// 定时发送开关 选择复选框
void MainWindow::on_chkTimSend_stateChanged(int arg1)
{
    // 获取复选框状态，未选为0，选中为2
    if(arg1 == 0){
        timSend->stop();
        // 时间输入框恢复可选
        ui->txtSendMs->setEnabled(true);
    }else{
        // 对输入的值做限幅，小于10ms会弹出对话框提示
        if(ui->txtSendMs->text().toInt() >= 10){
            timSend->start(ui->txtSendMs->text().toInt());// 设置定时时长，重新计数
            // 让时间输入框不可选，避免误操作（输入功能不可用，控件背景为灰色）
            ui->txtSendMs->setEnabled(false);
        }else{
            ui->chkTimSend->setCheckState(Qt::Unchecked);
            QMessageBox::critical(this, "错误提示", "定时发送的最小间隔为 10ms\r\n请确保输入的值 >=10");
        }
    }
}


//清除接收的信息
void MainWindow::on_chearBt_clicked()
{
    ui->recvEdit->clear();
    // 清除发送、接收字节计数
    recvNum = 0;
    // 状态栏显示计数值
    setNumOnLabel(lblRecvNum, "已接收(字节):", recvNum);
}


//保存文件
void MainWindow::on_chearBt_2_clicked()
{
    QString text = ui->recvEdit->toPlainText();
    // 生成带时间戳的默认文件名 (格式: file_20240101_123456.txt)
    QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss");
    QString defaultFileName = QString("file_%1.txt").arg(timestamp);
    // 弹出文件选择对话框，设置默认文件名
    QString filePath = QFileDialog::getSaveFileName(
        this,
        "Save File",
        defaultFileName,  // 默认文件名
        "Text Files (*.txt);;All Files (*)"
        );
    if (filePath.isEmpty()) {
        // 用户取消了保存操作
        return;
    }
    // 确保文件后缀是 .txt（如果用户没有手动输入后缀）
    if (!filePath.endsWith(".txt", Qt::CaseInsensitive)) {
        filePath += ".txt";
    }
    // 打开文件进行写入
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "Error", "Failed to open file for writing");
        return;
    }
    // 使用 QTextStream 写入内容
    QTextStream out(&file);
    out << text;
    file.close();
    QMessageBox::information(this, "Success", "File saved successfully");
}


//复选框 定时换行
void MainWindow::on_chk_rev_line_stateChanged(int arg1)
{
    // 获取复选框状态，未选为0，选中为2
    if(arg1 == 0){
        timer_line->stop();
    }else{
        // 对输入的值做限幅，小于10ms会弹出对话框提示
        if(ui->txtline_ms->text().toInt() >= 1)
        {
            timer_line->start(ui->txtline_ms->text().toInt());// 设置定时时长，重新计数
        }else
        {
            ui->chk_rev_line->setCheckState(Qt::Unchecked);
        }
    }
}

//创建LOG文件
void MainWindow::on_Create_log_clicked()
{
    if (isFileMode) {
        // 退出文件模式
        isFileMode = false;
        ui->Create_log->setText("创建LOG记录");
        ui->Create_log->setStyleSheet(""); // 清除绿色背景
        updateTimer.stop();
        if (logFile.isOpen()) logFile.close();
    } else {
        // 进入文件模式

        QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmm"); // 格式如：20231225_1530
        QString defaultFileName = QString("Log_%1.log").arg(timestamp);
        QString path = QFileDialog::getSaveFileName(
            this,
            "Create Log File",
            defaultFileName,  // 设置默认文件名
            "Log Files (*.log);;All Files (*)"
            );
        if (path.isEmpty()) return;
        // 确保文件后缀是.log（如果用户未输入后缀）
        if (!path.endsWith(".log", Qt::CaseInsensitive)) {
            path += ".log";
        }
        logFile.setFileName(path);
        if (!logFile.open(QIODevice::Append | QIODevice::Text)) {
            qWarning() << "Failed to create log file:" << path;
            return;
        }

        isFileMode = true;
        // ui->Create_log->setStyleSheet("background-color: green;"); // 按钮变绿
        ui->Create_log->setStyleSheet(
            "QPushButton {"
            "   background-color: yellow;"  // 背景黄
            "   border: 1px solid #FFA500;"  // 橙色边框
            "   border-radius: 5px;"  // 圆角
            "   padding: 3px;"  // 内边距
            "   color: black;"                    // 新增字体黑色 <--- 关键修改
            "}"
            "QPushButton:hover {"
            "   background-color: #FFD700;"  // 悬停时更亮的黄
            "   border: 1px solid orange;"
            "}"
            );
        ui->Create_log->setText("LOG记录记录中...");
        updateTimer.start();
        ui->recvEdit->clear();
    }
}

//

// 直接显示模式
const int maxChars = 1000000; // 最大字符串长度
void MainWindow::appendDirect(QString text)
{

    ui->recvEdit->moveCursor(QTextCursor::End);
    ui->recvEdit->insertPlainText(text);


    //如果超出范围了就截掉之前的值
    if (ui->recvEdit->toPlainText().length() > maxChars)
    {
        QTextCursor cursor(ui->recvEdit->document());
        cursor.setPosition(0);
        cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, ui->recvEdit->toPlainText().length() - maxChars);
        cursor.removeSelectedText();
    }


}


// HEX模式在窗口自动换行 实际数据不会换行
void MainWindow::on_chk_rev_hex_stateChanged(int arg1)
{
        // 获取复选框状态，未选为0，选中为2
        if(arg1 == 0)
        {
            ui->recvEdit->setLineWrapMode(QPlainTextEdit::NoWrap);  // 不自动换行
        }else
        {
            ui->recvEdit->setLineWrapMode(QPlainTextEdit::WidgetWidth); // 根据控件宽度自动换行

        }


}



void MainWindow::Set_qCustomPlot_Dark(bool enabled)
{

    if(enabled)
    {

        // 设置坐标轴、网格和文本颜色
        QColor bkColor("#1E1E1E"); // 背景色
        QColor bkColor2("#353535"); // 背景色
        QColor textColor("#ffffff"); // 白色文本

        // 1. 设置黑色背景(Dark Mode)
        ui->qCustomPlot->setBackground(QBrush(bkColor));
        ui->qCustomPlot->axisRect()->setBackground(QBrush(bkColor2));
        // 2. 设置坐标轴文字和刻度颜色
        ui->qCustomPlot->xAxis->setTickLabelColor(textColor);
        ui->qCustomPlot->yAxis->setTickLabelColor(textColor);
        ui->qCustomPlot->xAxis->setLabelColor(textColor);  // 坐标轴标题颜色
        ui->qCustomPlot->yAxis->setLabelColor(textColor);
        // 3. 设置坐标轴线颜色
        ui->qCustomPlot->xAxis->setBasePen(QPen(textColor, 1));
        ui->qCustomPlot->yAxis->setBasePen(QPen(textColor, 1));
        // 4. 设置图例文本颜色（关键！）
        ui->qCustomPlot->legend->setTextColor(textColor);//文本标签
        ui->qCustomPlot->legend->setBrush(QBrush(QColor(80, 80, 80, 204))); // 图例背景


        // 1. 设置黑色背景(Dark Mode)
        ui->Test->setBackground(QBrush(bkColor));
        ui->Test->axisRect()->setBackground(QBrush(bkColor2));
        ui->Test->xAxis->setTickLabelColor(textColor);
        ui->Test->yAxis->setTickLabelColor(textColor);
        ui->Test->xAxis->setLabelColor(textColor);  // 坐标轴标题颜色
        ui->Test->yAxis->setLabelColor(textColor);
        ui->Test->xAxis->setBasePen(QPen(textColor, 1));
        ui->Test->yAxis->setBasePen(QPen(textColor, 1));
        ui->Test->legend->setTextColor(textColor);//文本标签
        ui->Test->legend->setBrush(QBrush(QColor(80, 80, 80, 204))); // 图例背景

        // 适配黑色背景（Dark Mode）的颜色列表（16种高对比度颜色）
        colors = {
            QColor(0, 170, 255),     // 亮蓝色 (更鲜艳)
            QColor(255, 85, 85),     // 亮红色
            QColor(85, 255, 85),     // 亮绿色
            QColor(85, 255, 255),    // 亮青色
            QColor(255, 85, 255),    // 亮紫色
            QColor(255, 255, 85),    // 亮黄色
            QColor(170, 170, 255),   // 淡蓝色 (避免过多高饱和度)
            QColor(255, 170, 0),     // 橙色
            QColor(0, 255, 170),     // 蓝绿色
            QColor(170, 255, 0),     // 黄绿色
            QColor(255, 0, 170),     // 玫红色
            QColor(170, 0, 255),     // 深紫色
            QColor(255, 170, 170),   // 浅红色（避免过强对比）
            QColor(170, 255, 170),   // 浅绿色
            QColor(170, 170, 170),   // 中灰色（用于辅助线）
            QColor(255, 255, 255)    // 白色（确保在黑色背景下清晰）
        };

    }
    else
    {
        // 设置坐标轴、网格和文本颜色
        QColor bkColor("#F5F5F5"); // 背景色
        QColor bkColor2("#F0F0F0"); // 背景色
        QColor textColor("#000000"); // 白色文本


        // 1. 设置白色背景(Dark Mode)
        ui->qCustomPlot->setBackground(QBrush(bkColor));
        ui->qCustomPlot->axisRect()->setBackground(QBrush(bkColor2));
        // 2. 设置坐标轴文字和刻度颜色
        ui->qCustomPlot->xAxis->setTickLabelColor(textColor);
        ui->qCustomPlot->yAxis->setTickLabelColor(textColor);
        ui->qCustomPlot->xAxis->setLabelColor(textColor);  // 坐标轴标题颜色
        ui->qCustomPlot->yAxis->setLabelColor(textColor);
        // 3. 设置坐标轴线颜色
        ui->qCustomPlot->xAxis->setBasePen(QPen(textColor, 1));
        ui->qCustomPlot->yAxis->setBasePen(QPen(textColor, 1));
        // 4. 设置图例文本颜色（关键！）
        ui->qCustomPlot->legend->setTextColor(textColor);//文本标签
        ui->qCustomPlot->legend->setBorderPen(QPen(Qt::black));  // 图例边框
        ui->qCustomPlot->legend->setBrush(QBrush(QColor(250, 250, 250, 204))); // 图例背景


        // 1. 设置黑色背景(Dark Mode)
        ui->Test->setBackground(QBrush(bkColor));
        ui->Test->axisRect()->setBackground(QBrush(bkColor2));

        ui->Test->xAxis->setTickLabelColor(textColor);
        ui->Test->yAxis->setTickLabelColor(textColor);
        ui->Test->xAxis->setLabelColor(textColor);  // 坐标轴标题颜色
        ui->Test->yAxis->setLabelColor(textColor);
        ui->Test->xAxis->setBasePen(QPen(textColor, 1));
        ui->Test->yAxis->setBasePen(QPen(textColor, 1));
        ui->Test->legend->setTextColor(textColor);//文本标签
        ui->Test->legend->setBorderPen(QPen(Qt::black));  // 图例边框
        ui->Test->legend->setBrush(QBrush(QColor(250, 250, 250, 204))); // 图例背景

        // // 定义颜色列表（支持最多16个波形）
        colors = {
            Qt::blue,
            Qt::red,
            Qt::green,
            Qt::cyan,
            Qt::magenta,
            Qt::yellow,
            Qt::darkBlue,
            Qt::darkRed,
            Qt::darkGreen,
            Qt::darkCyan,
            Qt::darkMagenta,
            Qt::darkYellow,
            Qt::gray,
            Qt::black,
            Qt::lightGray,
            Qt::darkGray
        };

    }
    ui->qCustomPlot->replot();
    ui->Test->replot();
}



/****************FFT**********************/
//不好用
// 在类定义中添加成员变量
QCPItemStraightLine *cursorLine;
double cursorPosition; // 记录当前位置
void MainWindow::setupFFTCursor()
{
    // 创建垂直游标线
    cursorLine = new QCPItemStraightLine(ui->Test);
    cursorLine->setPen(QPen(Qt::red, 1, Qt::DashLine));

    // 初始位置设为0Hz
    cursorPosition = 0;
    updateCursorPosition();

    // 使能拖动交互
    ui->Test->setInteraction(QCP::iRangeDrag, true);
    ui->Test->setInteraction(QCP::iRangeZoom, true);

    // 连接鼠标移动信号
    connect(ui->Test, &QCustomPlot::mouseMove, this, &MainWindow::onFFTPlotMouseMove);
}
void MainWindow::updateCursorPosition()
{
    // 更新游标线位置
    cursorLine->point1->setCoords(cursorPosition, 0);
    cursorLine->point2->setCoords(cursorPosition, ui->Test->yAxis->range().upper);

    // 同时可以在这里显示当前频率点的幅值
    QToolTip::showText(QCursor::pos(),
                       QString("Freq: %1 Hz\nAmpl: %2").arg(cursorPosition).arg(getAmplitudeAt(cursorPosition)));



    ui->Test->replot();
}
double MainWindow::getAmplitudeAt(double freq)
{
    // 实现根据频率查找最近的幅值
    // 需要根据实际的FFT数据实现
    return 0.0;
}
void MainWindow::onFFTPlotMouseMove(QMouseEvent *event)
{
    if(event->buttons() & Qt::LeftButton){
        // 鼠标左键拖动时更新游标位置
        double x = ui->Test->xAxis->pixelToCoord(event->pos().x());
        cursorPosition = qBound(0.0, x, ui->Test->xAxis->range().upper);
        updateCursorPosition();
    }
}









#include <QOpenGLContext>

void MainWindow::on_DrawPlot_clicked()
{

    // 获取QPlainTextEdit中的文本
    QString text = ui->recvEdit->toPlainText();
    QStringList lines = text.split('\n', Qt::SkipEmptyParts);

    // 获取 QLineEdit 的文本并转换为整数
    bool ok;
    int maxLines = ui->Plot_num_Edit->text().toInt(&ok);
    // 验证输入是否有效（防止空文本或非数字）
    if (!ok || maxLines <= 0) {
        maxLines = 1024; // 默认值
        ui->Plot_num_Edit->setText("1024"); // 可选：重置为默认值
    }
    // 计算起始行
    int startLine = qMax(0, lines.size() - maxLines);
    lines = lines.mid(startLine);

    // 动态存储所有波形的数据
    QVector<QVector<double>> xValues;  // 所有波形的X轴值
    QVector<QVector<double>> yValues;  // 所有波形的Y轴值
    int maxWaveCount = 0;              // 最大波形数量

    // 第一轮解析：确定最大波形数量
    for (const QString &line : lines) {
        QStringList values = line.split(QRegularExpression("[,，\\s]+"), Qt::SkipEmptyParts);
        maxWaveCount = qMax(maxWaveCount, values.size());
    }

    // 初始化数据容器
    for (int i = 0; i < maxWaveCount; ++i) {
        xValues.append(QVector<double>());
        yValues.append(QVector<double>());
    }

    // 第二轮解析：填充数据
    double xValue = 0;  // X轴值
    for (const QString &line : lines) {
        QStringList values = line.split(QRegularExpression("[,，\\s]+"), Qt::SkipEmptyParts);

        for (int i = 0; i < values.size(); ++i) {
            if (i >= maxWaveCount) break; // 确保不越界

            bool ok = false;
            double val = values[i].toDouble(&ok);

            if (ok) {
                xValues[i].append(xValue);
                yValues[i].append(val);
            }
        }
        xValue += 1.0; // 每行X轴递增1
    }

    // 清除原有图形
    ui->qCustomPlot->clearGraphs();

    QPen graphPen;
    // 添加所有波形
    for (int i = 0; i < maxWaveCount; ++i) {
        ui->qCustomPlot->addGraph();
        ui->qCustomPlot->graph(i)->setData(xValues[i], yValues[i]);

        // 设置线条颜色（循环使用颜色列表）
        // ui->qCustomPlot->graph(i)->setPen(QPen(colors[i % colors.size()]));
        // ui->qCustomPlot->graph(i)->setPen(QPen(colors[i % colors.size()]));
        graphPen.setColor(colors[i % colors.size()]);
        graphPen.setWidth(2);  // 设置线宽
        ui->qCustomPlot->graph(i)->setPen(graphPen);  // 使用自定义Pen


        ui->qCustomPlot->graph(i)->setName(QString("波形%1").arg(i + 1));
    }

    // 设置坐标轴标签
    // ui->qCustomPlot->xAxis->setLabel("X轴");
    // ui->qCustomPlot->yAxis->setLabel("Y轴");

    // 设置坐标轴范围
    ui->qCustomPlot->xAxis->setRange(xValue-100, xValue);

    //计算Y轴范围（找到所有波形的最小和最大值）
    if (!yValues.isEmpty()) {
        double minY = std::numeric_limits<double>::max();
        double maxY = std::numeric_limits<double>::lowest();

        for (const auto &wave : yValues) {
            if (!wave.isEmpty()) {
                minY = qMin(minY, *std::min_element(wave.begin(), wave.end()));
                maxY = qMax(maxY, *std::max_element(wave.begin(), wave.end()));
            }
        }

        if (minY != std::numeric_limits<double>::max() &&
            maxY != std::numeric_limits<double>::lowest()) {
            ui->qCustomPlot->yAxis->setRange(minY - 1, maxY + 1); // 加一些边距
        } else {
            ui->qCustomPlot->yAxis->setRange(-1, 1); // 默认范围
        }
    }



    // 显示图例
    ui->qCustomPlot->legend->setVisible(true);

    // 重绘图表
    ui->qCustomPlot->replot();


}



void MainWindow::on_OpenGL_bt_stateChanged(int arg1)
{
    // 获取复选框状态，未选为0，选中为2
    if(arg1 == 0){
        ui->qCustomPlot->setOpenGl(false);  //  off GPU加速
        // ui->Test->setOpenGl(false);  // GPU加速
    }else{
        // 对输入的值做限幅，小于10ms会弹出对话框提示
        ui->qCustomPlot->setOpenGl(true);  // 启用GPU加速
        // ui->Test->setOpenGl(true);  // 启用GPU加速
    }
}


void MainWindow::on_sendcmdBt_clicked()
{

    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->CmdEdit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        QMessageBox::warning(this, "Error", "Invalid CMD format");
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    // 2. 收集所有DW参数
    QVector<QLineEdit*> dwEdits = {
        ui->dw0Edit, ui->dw1Edit, ui->dw2Edit,
        ui->dw3Edit, ui->dw4Edit, ui->dw5Edit,
        ui->dw6Edit, ui->dw7Edit
    };
    QByteArray packet = cmd; // 命令头部
    // 3. 处理每个DW（强制2字节）
    for(QLineEdit* edit : dwEdits) {
        QString hexStr = edit->text().remove(" ");
        if(!hexStr.isEmpty()) {
            // 限制最多输入4个HEX字符（2字节）
            QByteArray dwData = QByteArray::fromHex(hexStr.left(4).toLatin1());

            // 强制对齐到2字节
            if(dwData.isEmpty()) {
                continue; // 无效HEX跳过
            } else if(dwData.size() < 2) {
                dwData.append(QByteArray(2 - dwData.size(), 0x00));
            } else if(dwData.size() > 2) {
                dwData = dwData.left(2); // 截断
            }

            packet.append(dwData);
        }
    }
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
       int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
       // 发送字节计数并显示
       if(a > 0)
       {
           // 发送字节计数
           sendNum += a;
           // 状态栏显示计数值
           setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
       }
    } else {
        QMessageBox::critical(this, "Error", "Serial port not open");
    }

}


void MainWindow::on_LogYBox_stateChanged(int arg1)
{
    // 获取复选框状态，未选为0，选中为2
    if(arg1 == 0){

    }else{

    }
}




void MainWindow::on_autoDraw_stateChanged(int arg1)
{
    // 获取复选框状态，未选为0，选中为2
    if(arg1 == 0){

    }else{

    }
}


void MainWindow::on_DoFFTButton_clicked()
{

    // 获取QPlainTextEdit中的文本
    QString text = ui->recvEdit->toPlainText();
    QStringList lines = text.split('\n', Qt::SkipEmptyParts);

    // 获取 QLineEdit 的文本并转换为整数
    bool ok;
    int maxLines = ui->FFTNum_Edit->text().toInt(&ok);
    // 验证输入是否有效（防止空文本或非数字）
    if (!ok || maxLines <= 0) {
        maxLines = 1024; // 默认值
        ui->FFTNum_Edit->setText("1024"); // 可选：重置为默认值
    }

    int fs_num = ui->DataFs_Edit->text().toInt(&ok);
    // 验证输入是否有效（防止空文本或非数字）
    if (!ok || fs_num <= 0) {
        fs_num = 1024; // 默认值
        ui->DataFs_Edit->setText("1024"); // 可选：重置为默认值
    }


    // 计算起始行
    int startLine = qMax(0, lines.size() - maxLines);
    lines = lines.mid(startLine);

    // 动态存储所有波形的数据
    QVector<QVector<double>> xValues;  // 所有波形的X轴值
    QVector<QVector<double>> yValues;  // 所有波形的Y轴值
    int maxWaveCount = 0;              // 最大波形数量

    // 第一轮解析：确定最大波形数量
    for (const QString &line : lines) {
        QStringList values = line.split(QRegularExpression("[,，\\s]+"), Qt::SkipEmptyParts);
        maxWaveCount = qMax(maxWaveCount, values.size());
    }

    // 初始化数据容器
    for (int i = 0; i < maxWaveCount; ++i) {
        xValues.append(QVector<double>());
        yValues.append(QVector<double>());
    }

    // 第二轮解析：填充数据
    double xValue = 0;  // X轴值
    for (const QString &line : lines) {
        QStringList values = line.split(QRegularExpression("[,，\\s]+"), Qt::SkipEmptyParts);

        for (int i = 0; i < values.size(); ++i) {
            if (i >= maxWaveCount) break; // 确保不越界

            bool ok = false;
            double val = values[i].toDouble(&ok);

            if (ok) {
                xValues[i].append(xValue);
                yValues[i].append(val);
            }
        }
        xValue += 1.0; // 每行X轴递增1
    }


    ui->Test->clearPlottables();  // 清除所有图形（包括柱状图）
    // 为每个通道计算并显示频谱(柱状图版本)
    for (int waveIdx = 0; waveIdx < maxWaveCount; ++waveIdx)
    {
        // 检查是否有该通道数据
        if (waveIdx >= yValues.size() || yValues[waveIdx].isEmpty()) continue;

        // 计算FFT频谱
        QVector<double> spectrum;

        // FFTNum_Edit


        qDebug() << "Input Size" <<yValues[waveIdx].size();
        // --- Step 1: 计算频谱图 ---
        computeFFT(yValues[waveIdx], spectrum, maxLines);//
        // --- Step 2: 幅度计算并归一化 ---

        if(ui->ForOneYBox->checkState() == Qt::Checked)//ForOneYBox
        {
            const double normFactor = 1.0 / maxLines;  // 双边谱归一化系数
            for (int i = 0; i < spectrum.size(); i++) {
                double magnitude = std::abs(spectrum[i]) * normFactor;  // |FFT[k]| / N
                // 仅对正频率部分 (i > 0) 做 ×2 单边谱归一化
                if (i > 0 && i < maxLines / 2) {
                    magnitude *= 2;
                }
                spectrum[i] = magnitude;
            }
        }


        // 创建频率轴
        double sampleRate = fs_num;

        QVector<double> freqAxis(spectrum.size());


        for (int i = 0; i < spectrum.size(); ++i) {
            freqAxis[i] = i * sampleRate / (maxLines);
        }

        // // 创建柱状图(替代原来的graph())
        // QCPBars *bars = new QCPBars(ui->Test->xAxis, ui->Test->yAxis);

        // // 设置样式
        // // QColor color = QColor::fromHsv(waveIdx * 255 / maxWaveCount, 255, 200);
        // QColor color = colors[waveIdx % colors.size()];
        // bars->setPen(QPen(color.darker(110), 1));  // 边框颜色
        // bars->setBrush(QBrush(color));            // 填充颜色
        // // 设置柱宽（根据频率分辨率自动计算）
        // double binWidth = sampleRate/(2 * spectrum.size());
        // bars->setWidth(binWidth * 0.5);  // 柱子占80%的宽度，保留间隔

        // // 设置通道名称
        // bars->setName(QString("通道 %1").arg(waveIdx+1));


        // //可选：对Y轴取对数显示
        // //根据LogYBox是否勾选确定
        // // 获取是否启用对数显示（假设 LogYBox 是一个 QCheckBox）
        // if (ui->LogYBox->checkState() == Qt::Checked)
        // {

        //     QVector<double> logSpectrum;
        //     for (double val : spectrum) {
        //         logSpectrum << 20 * log10(val + 1e-4);  // 避免log(0)
        //     }
        //     bars->setData(freqAxis, logSpectrum);
        //     // 可选：设置Y轴为对数刻度（需启用对数坐标轴）
        //     // ui->Test->yAxis->setScaleType(QCPAxis::stLogarithmic);
        //     ui->Test->yAxis->setLabel("Amplitude (dB)");  // 更新轴标签
        // }
        // else
        // {
        //     // 设置数据
        //     bars->setData(freqAxis, spectrum);  // x,y值
        //     // 恢复线性坐标轴
        //     // ui->Test->yAxis->setScaleType(QCPAxis::stLinear);
        //     ui->Test->yAxis->setLabel("Amplitude");
        // }

        // 根据LogYBox状态选择绘制方式
        if (ui->LogYBox->checkState() == Qt::Checked)
        {
            // 对数模式 - 使用曲线图(Line Graph)
            QCPGraph *graph = ui->Test->addGraph();

            // 对Y值取对数
            QVector<double> logSpectrum;
            for (double val : spectrum) {
                logSpectrum << 20 * log10(val + 1e-4);  // 避免log(0)
            }

            // 设置曲线样式
            QColor color = colors[waveIdx % colors.size()];
            graph->setPen(QPen(color, 2));  // 设置线条颜色和宽度
            graph->setName(QString("通道 %1").arg(waveIdx+1));
            graph->setData(freqAxis, logSpectrum);

            // 可以添加曲线点标记(可选)
            graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 5));
        }
        else
        {
            // 线性模式 - 使用柱状图
            QCPBars *bars = new QCPBars(ui->Test->xAxis, ui->Test->yAxis);

            QColor color = colors[waveIdx % colors.size()];
            bars->setPen(QPen(color.darker(110), 1));
            bars->setBrush(QBrush(color));

            double binWidth = sampleRate/(2 * spectrum.size());
            bars->setWidth(binWidth * 0.5);
            bars->setName(QString("通道 %1").arg(waveIdx+1));
            bars->setData(freqAxis, spectrum);
        }



    }

    // 设置坐标轴标签和范围
    ui->Test->xAxis->setLabel("频率 (Hz)");
    ui->Test->yAxis->setLabel(ui->LogYBox->checkState() == Qt::Checked ? "Amplitude (dB)" : "Amplitude");

    ui->Test->rescaleAxes();
    double currentMax = ui->Test->yAxis->range().upper;
    ui->Test->yAxis->setRangeUpper(currentMax * 1.2);
    currentMax = ui->Test->xAxis->range().upper;
    ui->Test->xAxis->setRangeUpper(currentMax * 1.2);


    if (ui->LogYBox->checkState() != Qt::Checked)
    {
        // 柱状图需要从0开始
        ui->Test->xAxis->setRangeLower(0);
    }

    ui->Test->replot();// 重绘图表
    // 自动调整范围

    // setupFFTCursor();      // 设置游标功能
    ui->Test->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);  // 保留拖动和缩放
    // ui->Test->axisRect()->setRangeDrag(Qt::Horizontal);           // 仅水平拖动
    ui->Test->axisRect()->setRangeZoom(Qt::Horizontal);           // 可选的：仅水平缩放
}


void MainWindow::on_SentDW0BT_clicked()
{

    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw0Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 0Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_0为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }


}


void MainWindow::on_SentDW1BT_clicked()
{
    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw1Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 1Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_1为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }
}


void MainWindow::on_SentDW2BT_clicked()
{
    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw2Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 0Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_2为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }
}


void MainWindow::on_SentDW3BT_clicked()
{
    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw3Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 0Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_3为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }
}


void MainWindow::on_SentDW4BT_clicked()
{
    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw4Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 0Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_4为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }
}


void MainWindow::on_SentDW5BT_clicked()
{
    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw5Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 0Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_5为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }
}


void MainWindow::on_SentDW6BT_clicked()
{
    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw6Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 0Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_6为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }
}


void MainWindow::on_SentDW7BT_clicked()
{
    // 1. 处理CMD（强制2字节）
    QString cmdHex = ui->dw7Edit->text().remove(" ");
    QByteArray cmd = QByteArray::fromHex(cmdHex.left(4).toLatin1()); // 最多取4个HEX字符（2字节）

    if(cmd.isEmpty() || cmdHex.isEmpty()) {
        // QMessageBox::warning(this, "Error", "Invalid 0Edit format");
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "U16_7为空", this, QRect(), 2000); // 2秒后消失
        return;
    }
    if(cmd.size() < 2) {
        cmd.append(QByteArray(2 - cmd.size(), 0x00));
    } else if(cmd.size() > 2) {
        cmd = cmd.left(2); // 超过2字节截断
    }
    QByteArray packet = cmd; // 命令头部
    // 如发送成功，会返回发送的字节长度。失败，返回-1。
    if(serialPort && serialPort->isOpen()) {
        int a = serialPort->write(packet);
        qDebug() << "Sent:" << packet.toHex(' ');
        // 发送字节计数并显示
        if(a > 0)
        {
            // 发送字节计数
            sendNum += a;
            // 状态栏显示计数值
            setNumOnLabel(lblSendNum, "已发送(字节): ", sendNum);
        }
    } else {
        // QMessageBox::critical(this, "Error", "Serial port not open");
        // 在鼠标当前位置显示短暂提示（适用于非阻塞提示）
        QPoint pos = QCursor::pos();
        QToolTip::showText(pos, "串口未打开", this, QRect(), 2000); // 2秒后消失
    }
}

